package com.company.project.util;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTCreationException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwt.interfaces.JWTVerifier;
import com.company.project.model.User;

import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/*
 * @Description: JSON Web令牌（JWT）是一个开放标准（RFC 7519），它定义了一种紧凑的、自包含的方式，
 * 用于在各方之间作为JSON对象安全地传输信息。该信息可以被验证和信任，因为它是数字签名的。JWTS可以使用秘密
 * （使用HMAC算法）或公钥/私钥对使用RSA或ECDSA来签名。
 * 参考：https://blog.csdn.net/ljk126wy/article/details/82751787
 * @Author:         brilliantzhao
 * @CreateDate:     2019/6/5 14:19
 * @UpdateUser:
 * @UpdateDate:     2019/6/5 14:19
 * @UpdateRemark:
 */
public class JWTUtil {

    /*
     * @Description: 创建不携带自定义信息的 token
     * @Author: brilliantzhao
     * @CreateDate: 2019/6/5 14:54
     * @Param:
     * @Return:
     */
    public String createToken() {
        try {
            // 1. 构建密钥信息
            String secret = "secret";//token 密钥
            Algorithm algorithm = Algorithm.HMAC256(secret);
            // 2. 构建头部信息
            Map<String, Object> map = new HashMap<String, Object>();
            map.put("alg", "HS256");
            map.put("typ", "JWT");
            // 3. 通过定义注册和自定义声明 并组合头部信息和密钥信息生成jwt token
            Date nowDate = new Date();
            // 有效期
            Date expireDate = getAfterDate(nowDate, 0, 0, 0, 2, 0, 0);
            String token = JWT.create()
                    /*设置头部信息 Header*/
                    .withHeader(map)
                    /*设置 载荷 Payload*/
                    .withIssuer("SERVICE")//签名是有谁生成 例如 服务器
                    .withSubject("this is test token")//签名的主题
                    //.withNotBefore(new Date())//定义在什么时间之前，该jwt都是不可用的.
                    .withAudience("APP")//签名的观众 也可以理解谁接受签名的
                    .withIssuedAt(nowDate) //生成签名的时间
                    .withExpiresAt(expireDate)//签名过期的时间
                    /*签名 Signature */
                    .sign(algorithm);
            return token;
        } catch (JWTCreationException exception) {
            exception.printStackTrace();
        }
        return null;
    }

    /*
     * @Description: 创建携带自定义信息的 token
     * @Author: brilliantzhao
     * @CreateDate: 2019/6/5 14:54
     * @Param:
     * @Return:
     */
    public String createTokenWithClaim(User user, String password) {
        try {
            // 1. 构建密钥信息
            // 以 password 作为 token 的密钥
            String secret = password;//token 密钥
            Algorithm algorithm = Algorithm.HMAC256(secret);
            // 2. 构建头部信息
            Map<String, Object> map = new HashMap<String, Object>();
            map.put("alg", "HS256");
            map.put("typ", "JWT");
            // 3. 通过定义注册和自定义声明 并组合头部信息和密钥信息生成jwt token
            Date nowDate = new Date();
            // 有效期
            Date expireDate = getAfterDate(nowDate, 0, 0, 0, 0, 0, 10);
            String token = JWT.create()
                    /*设置头部信息 Header*/
                    .withHeader(map)
                    /*设置 载荷 Payload*/
                    .withClaim("loginName", "brilliantzhao")
                    .withIssuer("SERVICE")//签名是有谁生成 例如 服务器
                    .withSubject("this is test token")//签名的主题
                    //.withNotBefore(new Date())//定义在什么时间之前，该jwt都是不可用的.
                    .withAudience(user.getId() + "")//签名的观众 也可以理解谁接受签名的,将 user id 保存到 token 里面
                    .withIssuedAt(nowDate) //生成签名的时间
                    .withExpiresAt(expireDate)//签名过期的时间
                    /*签名 Signature */
                    .sign(algorithm);
            return token;
        } catch (JWTCreationException exception) {
            exception.printStackTrace();
        }
        return null;
    }

    /**
     * 返回一定时间后的日期
     *
     * @param date   开始计时的时间
     * @param year   增加的年
     * @param month  增加的月
     * @param day    增加的日
     * @param hour   增加的小时
     * @param minute 增加的分钟
     * @param second 增加的秒
     * @return
     */
    public Date getAfterDate(Date date, int year, int month, int day, int hour, int minute, int second) {
        if (date == null) {
            date = new Date();
        }

        Calendar cal = new GregorianCalendar();

        cal.setTime(date);
        if (year != 0) {
            cal.add(Calendar.YEAR, year);
        }
        if (month != 0) {
            cal.add(Calendar.MONTH, month);
        }
        if (day != 0) {
            cal.add(Calendar.DATE, day);
        }
        if (hour != 0) {
            cal.add(Calendar.HOUR_OF_DAY, hour);
        }
        if (minute != 0) {
            cal.add(Calendar.MINUTE, minute);
        }
        if (second != 0) {
            cal.add(Calendar.SECOND, second);
        }
        return cal.getTime();
    }

    /*
     * @Description: 验证 JWT Token
     * @Author: brilliantzhao
     * @CreateDate: 2019/6/5 15:01
     * @Param:
     * @Return:
     */
    public void verifyToken(String password, String token) {
        // 构建密钥信息
        Algorithm algorithm = Algorithm.HMAC256(password);
        // 通过密钥信息和签名的发布者的信息生成JWTVerifier (JWT验证类)
        JWTVerifier verifier = JWT.require(algorithm)
                .withIssuer("SERVICE")
                .build(); //Reusable verifier instance
        // 通过JWTVerifier 的verify获取 token中的信息。
        DecodedJWT jwt = verifier.verify(token);
        // 获取到我们之前生成token的 签名的主题，观众 和自定义的声明信息。
        String subject = jwt.getSubject();
        Map<String, Claim> claims = jwt.getClaims();
        Claim claim = claims.get("loginName");
        System.out.println(claim.asString());
        List<String> audience = jwt.getAudience();
        System.out.println(subject);
        System.out.println(audience.get(0));
    }

}
